# 05 - 权限系统

权限系统是 Claude Code 的安全核心，每次 Tool 调用都会经过权限检查。

---

## 权限模式 (PermissionMode)

| 模式 | 行为 |
|------|------|
| `default` | 写操作需要用户确认 |
| `plan` | 只允许只读操作，写操作被拒绝 |
| `bypassPermissions` | 跳过所有权限检查（危险） |
| `auto` | 自动批准，但经过安全分类器检查 |
| `acceptEdits` | 自动批准编辑，其他操作照常 |
| `dontAsk` | 不弹出确认，直接拒绝需要确认的操作 |

---

## 权限检查流程

```
Tool 被调用
  │
  ▼
1. validateInput()
   - 校验输入参数合法性
   - 与权限无关，纯数据校验
   - 失败 → 直接返回错误
  │
  ▼
2. 全局拒绝规则检查 (alwaysDenyRules)
   - 匹配 → 直接拒绝，不执行
  │
  ▼
3. checkPermissions() — Tool 级别
   - 每个 Tool 自己的权限逻辑
   - 返回: allow / deny / ask
  │
  ▼
4. 全局允许规则检查 (alwaysAllowRules)
   - 匹配 → 自动批准
  │
  ▼
5. 权限模式检查
   - bypassPermissions → 批准
   - plan 模式 + 写操作 → 拒绝
   - auto 模式 → 安全分类器检查
  │
  ▼
6. 用户确认 (ask)
   - 弹出权限对话框
   - 用户选择: Allow / Deny / Always Allow
```

---

## ToolPermissionContext

权限上下文定义了当前的权限规则集合：

```typescript
type ToolPermissionContext = DeepImmutable<{
  mode: PermissionMode
  additionalWorkingDirectories: Map<string, AdditionalWorkingDirectory>
  alwaysAllowRules: ToolPermissionRulesBySource   // 始终允许
  alwaysDenyRules: ToolPermissionRulesBySource     // 始终拒绝
  alwaysAskRules: ToolPermissionRulesBySource      // 始终询问
  isBypassPermissionsModeAvailable: boolean
  strippedDangerousRules?: ToolPermissionRulesBySource  // 被剥离的危险规则
  shouldAvoidPermissionPrompts?: boolean  // 后台 Agent 不能弹出 UI
  awaitAutomatedChecksBeforeDialog?: boolean
  prePlanMode?: PermissionMode  // Plan 模式前的权限模式
}>
```

**注意 `DeepImmutable`**：权限上下文是深度不可变的。Tool 不能修改权限上下文，只能通过 `contextModifier` 返回新的上下文。

---

## 权限规则示例

权限规则按来源组织 (`ToolPermissionRulesBySource`)，来源包括：
- `userSettings` — 用户配置 (settings.json)
- `projectSettings` — 项目配置 (.claude/settings.json)
- `localSettings` — 本地配置 (.claude/settings.local.json)
- `flagSettings` — 特性标志下发的配置
- `policySettings` — 组织策略
- `cliArg` — CLI 参数传入
- `command` — 斜杠命令设置
- `session` — 用户运行时选择 ("Always Allow")

规则格式：

```
Tool名称(模式)
```

例如：
- `Bash(git *)` — 允许以 `git` 开头的 Bash 命令
- `FileEdit(/src/**)` — 允许编辑 `/src/` 下的文件
- `Bash` — 允许所有 Bash 命令（无模式 = 全局拒绝/允许）

---

## preparePermissionMatcher

Tool 可以实现 `preparePermissionMatcher()` 来支持模式匹配：

```typescript
// BashTool 的例子：将 "git *" 匹配到 "git status"
preparePermissionMatcher(input) {
  return async (pattern: string) => {
    // 解析 input.command，与 pattern 做 glob 匹配
    return matchesPattern(input.command, pattern)
  }
}
```

这允许用户创建细粒度规则，如"允许 `git` 命令但拒绝 `rm`"。

---

## 安全设计原则

### 1. 失败关闭 (Fail-Closed)

```typescript
// 默认值：假设不安全
isConcurrencySafe() { return false }  // 默认不并发
isReadOnly() { return false }          // 默认假设有写入
isDestructive() { return false }       // 需要显式声明
```

Tool 必须**主动声明**自己是安全的，而不是默认安全。

### 2. 后台 Agent 不弹 UI

```typescript
shouldAvoidPermissionPrompts?: boolean
```

当设为 `true`（如后台子 Agent），权限提示会被自动拒绝而不是弹出确认框。

### 3. 拒绝追踪

```typescript
localDenialTracking?: DenialTrackingState
```

追踪连续拒绝次数。如果同一操作被连续拒绝多次，会触发降级行为，避免模型陷入无效重试循环。

### 4. 文件系统安全

- **UNC 路径处理**：延迟解析直到权限授予，防止 NTLM 凭证泄露
- **设备文件阻止**：禁止读取 `/dev/zero`、`/dev/stdin` 等设备文件
- **二进制文件检测**：自动跳过二进制文件（PDF/图片除外）
- **文件大小限制**：编辑操作最大 1 GiB

---

## Tool 过滤

在 Tool 列表提供给模型之前，会经过过滤：

```typescript
export function filterToolsByDenyRules(
  tools: readonly Tool[],
  permissionContext: ToolPermissionContext
): Tool[] {
  return tools.filter(tool => !getDenyRuleForTool(permissionContext, tool))
}
```

被全局拒绝的 Tool 不会出现在模型的可用工具列表中——模型根本不知道它们的存在。

---

## 权限与 Hooks 的关系

用户可以通过 Hooks 系统定义 `PreToolUse` 和 `PostToolUse` 钩子来自定义权限行为：

```
PreToolUse Hook → 可以 approve/deny/modify 输入
  ↓
内置权限检查 (checkPermissions)
  ↓
PostToolUse Hook → 可以在执行后审计
```

Hooks 在 [[11 - 配置与内存系统]] 中详细介绍。
